import ProductCommon from '@/api/ProductCommon';
import {
  Box,
  ClickUpload,
  DragAndDropProvider,
  DroppableContainer,
  Hover,
  HoverStyle,
  Icon,
  PopoverConfirm,
  useCall,
  useReq,
} from '@/components';
import { uid } from '@/utils';
import request from '@/utils/request';
import { Button, Input } from 'antd';
import { basename, extname } from 'path';
import React, { Children, memo, useEffect, useState } from 'react';

/**
 * @typedef {Object} FileItem
 * @property {String} id
 * @property {String} url
 * @property {String} name
 * @property {String} ext
 * @property {Number} size
 */

function FileListView({
  fileList = [],
  onChange,
  disabled = false,
  rowKey,
  nameKey,
  urlKey,
  sizeKey,
  extKey,
  hideName,
}) {
  const handleChange = useCall((index, key, value) => {
    const newList = fileList.slice();
    let _key = {
      name: nameKey,
      url: urlKey,
      size: sizeKey,
      extKey: extKey,
    }[key];
    newList[index] = { ...newList[index], [_key]: value };
    onChange(newList);
  });

  const handleRemove = useCall((index) => {
    const newList = fileList.slice();
    newList.splice(index, 1);
    onChange(newList);
  });

  const onItemProp = (item) => ({
    key: item[rowKey] || item.key,
    name: item[nameKey],
    url: item[urlKey],
    size: item[sizeKey],
    ext: item[extKey],
  });

  return (
    <DragAndDropProvider itemComponent={Box} onDrop={console.log}>
      <DroppableContainer>
        {fileList?.map((item, index) => {
          const prop = onItemProp(item);

          return (
            <FileItemView
              key={`sign:${prop.key}`}
              index={index}
              {...prop}
              hideName={hideName}
              disabled={disabled}
              onChange={handleChange}
              onRemove={handleRemove}
            />
          );
        })}
      </DroppableContainer>
    </DragAndDropProvider>
  );
}

FileItemView = memo(FileItemView);

function FileItemView({ onChange, onRemove, index, disabled = false, hideName = false, ...item }) {
  const { id, url, name, ext, size } = item;
  const onChangeName = useCall((e) => {
    // if (e.nativeEvent.isComposing === true) return;
    onChange(index, 'name', e.target.value);
  });

  return (
    <Hover>
      <Box flex dir='col' ali='stretch' py={1}>
        {!hideName && <Input placeholder='请输入文件名' value={name} onChange={onChangeName} readOnly={disabled} />}
        <Box flex dir='row' ali='center' jus='between' py={1}>
          <Button type='link' size='small' href={url} target='_blank' style={{ minWidth: 0, overflow: 'hidden' }}>
            {url?.split(/\//g).pop()}
          </Button>
          {!disabled && (
            <PopoverConfirm content='确认删除此文件？' onOk={useCall(onRemove, index)}>
              <HoverStyle hoverStyle={{ opacity: 1 }} style={{ opacity: 0 }}>
                <Button size='small' type='text' danger icon={<Icon type='close' />} />
              </HoverStyle>
            </PopoverConfirm>
          )}
        </Box>
      </Box>
    </Hover>
  );
}

function uploadFiles(files, options = null) {
  const formData = new FormData();
  for (const i in [].slice.call(files)) {
    formData.append('files', files[i], files[i].name || `file${i}`);
  }
  return request.post('/UploadAttachmentController/uploadFiles', formData);
}

/** UploadWithFilename */
function UploadWithFilename(props) {
  const {
    value: fileList = [],
    onChange,
    children,
    uploadBtn,
    accept,
    multiple,
    btnPlacement = 'after',
    rowKey = 'id',
    nameKey = 'name',
    urlKey = 'url',
    sizeKey = 'size',
    extKey = 'ext',
    hideName = false,
    onAdd,
  } = props;
  const [uploadingFiles, setUploading] = useState([]);
  const xhr = useReq(ProductCommon.uploadFiles);
  const progress = xhr.uploadProgress;
  let { readOnly, disabled } = props;
  disabled ??= readOnly ?? false;

  const onAddItem = (data) => {
    const item = {
      key: uid(),
      [urlKey]: data.url,
      [nameKey]: data.name,
      [sizeKey]: data.size,
      [extKey]: data.ext,
    };

    return {
      ...item,
      ...onAdd?.(item),
    };
  };

  const onUpload = useCall((files) => {
    const newFileList = [].map.call(files, (file) => {
      let { name, size } = file;
      let ext = '.unknown';
      name ||= 'untitled';
      try {
        ext = extname(name);
        name = basename(name, ext);
        ext = ext.substr(1);
      } catch {}

      return onAddItem({
        url: null,
        name,
        ext,
        size,
      });
    });
    setUploading(newFileList);
    xhr.start(files);
  });

  const resp = xhr.result?.data || null;
  useEffect(() => {
    if (resp?.length === uploadingFiles.length) {
      setUploading([]);
      const newFileList = uploadingFiles.map((item, index) => ({
        ...item,
        url: resp[index].path,
        [urlKey]: resp[index].path,
      }));
      onChange(multiple ? fileList.concat(newFileList) : newFileList);
    }
  }, [resp]);

  const childrenProps = { uploadingFiles, progress, fileList, onChange };

  let renderChildren = null;

  const count = Children.count(children);
  if (count === 1) {
    children.cloneElement(children, childrenProps);
  } else if (typeof children === 'function') {
    renderChildren = children(childrenProps);
  } else {
    renderChildren = (
      <>
        {!fileList?.length && disabled && <span className='c-text-secondary'>暂无数据</span>}
        <FileListView
          fileList={fileList}
          onChange={onChange}
          disabled={disabled}
          rowKey={rowKey}
          nameKey={nameKey}
          urlKey={urlKey}
          sizeKey={sizeKey}
          extKey={extKey}
          hideName={hideName}
        />
      </>
    );
  }

  const isAcceptList = /^(\.[a-z0-9]+,?)+$/gi.test(accept);
  const acceptList = accept?.split(/,?\./g) ?? [];

  const btn = (
    <>
      <ClickUpload onUpload={onUpload} loading={xhr.status === 'loading'} accept={accept} multiple={multiple}>
        {uploadBtn || (
          <Button icon={<Icon type='upload' />} loading={uploadingFiles.length > 0}>
            上传
          </Button>
        )}
      </ClickUpload>
      {isAcceptList && acceptList.length > 0 && (
        <Box py={0.5} className='c-text-secondary'>
          支持扩展名: {acceptList.join(' ')}
        </Box>
      )}
    </>
  );

  if (disabled) {
    return renderChildren;
  }

  return (
    <>
      {btnPlacement === 'before' && btn}
      {renderChildren}
      {btnPlacement === 'after' && btn}
    </>
  );
}

export default UploadWithFilename;
